
/* IMPORT */

import 'highlight.js/styles/github';
// import 'katex/dist/katex.min.css';
const _=require('lodash');
const CRC32=require('crc-32'); // Not a cryptographic hash function, but it's good enough (and fast!) for our purposes
const mermaid_m=require('mermaid');
const path=window.require("path");
const pify=window.require('pify');
const remark=window.require('remark');
const strip=require('strip-markdown');
const showdown =window.require('showdown');
const showdownHighlight =window.require('showdown-highlight');
const showdownKatex =window.require('showdown-katex-studdown');
const showdownTargetBlack =window.require('showdown-target-blank');
import Config from '../../common/config';

/* MARKDOWN */

const Markdown = {

  converter: undefined,

  extensions: {

    encodeSpecialLinks () { // Or they won't be parsed as images/links whatever

      return [{
        type: 'language',
        regex: `\\[([^\\]]*)\\]\\(((?:${Config.attachments.token}|${Config.notes.token}|${Config.tags.token})/[^\\)]*)\\)`,
        replace ( match, $1, $2 ) {
          return `[${$1}](${encodeURI ( $2 )})`;
        }
      }];

    },

    attachment () {

      const {path: attachmentsPath, token} = Config.attachments;

      if ( !attachmentsPath ) return [];

      return [
        { // Image
          type: 'output',
          regex: `<img(.*?)src="${token}/([^"]+)"(.*?)>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            const filePath = path.join ( attachmentsPath, $2 );
            return `<img${$1}src="file://${filePath}" class="attachment" data-filename="${$2}"${$3}>`;
          }
        },
        { // Link Button
          type: 'output',
          regex: `<a(.*?)href="${token}/([^"]+)"(.*?)></a>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            const basename = path.basename ( $2 );
            const filePath = path.join ( attachmentsPath, $2 );
            return `<a${$1}href="file://${filePath}" class="attachment button gray" data-filename="${$2}"${$3}><i class="icon small">paperclip</i><span>${basename}</span></a>`;
          }
        },
        { // Link
          type: 'output',
          regex: `<a(.*?)href="${token}/([^"]+)"(.*?)>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            const filePath = path.join ( attachmentsPath, $2 );
            return `<a${$1}href="file://${filePath}" class="attachment" data-filename="${$2}"${$3}><i class="icon xsmall">paperclip</i>`;
          }
        }
      ];

    },

    note () {

      const {path: notesPath, token} = Config.notes;

      if ( !notesPath ) return [];

      return [
        { // Link Button
          type: 'output',
          regex: `<a(.*?)href="${token}/([^"]+)"(.*?)></a>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            const basename = path.basename ( $2 );
            const filePath = path.join ( notesPath, $2 );
            return `<a${$1}href="file://${filePath}" class="note button gray" data-filepath="${filePath}"${$3}><i class="icon small">note</i><span>${basename}</span></a>`;
          }
        },
        { // Link
          type: 'output',
          regex: `<a(.*?)href="${token}/([^"]+)"(.*?)>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            const filePath = path.join ( notesPath, $2 );
            return `<a${$1}href="file://${filePath}" class="note" data-filepath="${filePath}"${$3}><i class="icon xsmall">note</i>`;
          }
        }
      ];

    },

    tag () {

      const {token} = Config.tags;

      return [
        { // Link Button
          type: 'output',
          regex: `<a(.*?)href="${token}/([^"]+)"(.*?)></a>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            return `<a${$1}href="#" class="tag button gray" data-tag="${$2}"${$3}><i class="icon small">tag</i><span>${$2}</span></a>`;
          }
        },
        { // Link
          type: 'output',
          regex: `<a(.*?)href="${token}/([^"]+)"(.*?)>`,
          replace ( match, $1, $2, $3 ) {
            $2 = decodeURI ( $2 );
            return `<a${$1}href="#" class="tag" data-tag="${$2}"${$3}><i class="icon xsmall">tag</i>`;
          }
        }
      ];

    },

    katex () {

      try {

        return showdownKatex ( Config.katex );

      } catch ( e ) {

        return `<p class="text-red">[KaTeX error: ${e.message}]</p>`;

      }

    },

    mermaid () {

      mermaid_m.initialize ( Config.mermaid );

      return [{
        type: 'language',
        regex: '```mermaid([^`]*)```',
        replace ( match, $1 ) {
          const id = `mermaid-${CRC32.str ( $1 )}`;
          try {
            const svg = mermaid_m.render ( id, $1 );
            return `<div class="mermaid">${svg}</div>`;
          } catch ( e ) {
            $(`#${id}`).remove ();
            return `<p class="text-red">[mermaid error: ${e.message}]</p>`;
          }
        }
      }];

    }

  },

  getConverter () {

    if ( Markdown.converter ) return Markdown.converter;

    const {encodeSpecialLinks, attachment, note, tag, katex, mermaid} = Markdown.extensions;

    const converter = new showdown.Converter ({
      metadata: true,
      extensions: [showdownHighlight, showdownTargetBlack, encodeSpecialLinks (),attachment (), note (), tag (), katex (), mermaid ()]
    });

    converter.setFlavor ( 'github' );

    Markdown.converter = converter;

    return converter;

  },

  render: _.memoize ( ( str: string ): string => {

    return Markdown.getConverter ().makeHtml ( str );

  }),

  strip: async ( str: string ): Promise<string> => {

    return ( await pify ( remark ().use ( strip ).process )( str ) ).toString ();

  }

};

/* EXPORT */

export default Markdown;
